package com.glSto.controller;

import com.google.gson.Gson;
import com.google.gson.GsonBuilder;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
import org.springframework.beans.factory.annotation.Value;
import org.springframework.jdbc.core.JdbcTemplate;
import org.springframework.jdbc.datasource.DriverManagerDataSource;
import org.springframework.web.bind.annotation.PostMapping;
import org.springframework.web.bind.annotation.RequestBody;
import org.springframework.web.bind.annotation.RequestMapping;
import org.springframework.web.bind.annotation.RestController;

import javax.net.ssl.*;
import javax.crypto.Cipher;
import java.io.*;
import java.net.URL;
import java.nio.charset.StandardCharsets;
import java.security.KeyFactory;
import java.security.cert.CertificateException;
import java.security.cert.X509Certificate;
import java.security.interfaces.RSAPublicKey;
import java.security.spec.X509EncodedKeySpec;
import java.util.*;

/**
 * 简化参数的门禁系统接口
 * 只需要四个请求参数：username、password、startSwingTime、endSwingTime
 */
@RestController
@RequestMapping("/api/access-control")
public class AccessControlSystem_ZS {
    private static final Logger logger = LoggerFactory.getLogger(AccessControlSystem_ZS.class);
    private static final Gson GSON = new GsonBuilder()
            .setDateFormat("yyyy-MM-dd HH:mm:ss")  // 支持空格分隔的日期格式
            .create();
    private static final String BEARER_PREFIX = "Bearer ";
    private static final String RSA_ALGORITHM = "RSA";
    private static final int RSA_MAX_ENCRYPT_BLOCK = 117;

    // 配置参数
    @Value("${access-control.public-key-api}")
    private String publicKeyApi;

    @Value("${access-control.oauth-token-api}")
    private String oauthTokenApi;

    @Value("${access-control.channel-query-api}")
    private String channelQueryApi;

    @Value("${access-control.access-count-api}")
    private String accessCountApi;

    @Value("${access-control.client-id}")
    private String clientId;

    @Value("${access-control.client-secret}")
    private String clientSecret;

    private static String grantType;

    @Value("${access-control.grant-type:password}")
    public void setGrantType(String grantType) {
        AccessControlSystem_ZS.grantType = grantType;
    }

    @Value("${access-control.db.url}")
    private String dbUrl;

    @Value("${access-control.db.username}")
    private String dbUsername;

    @Value("${access-control.db.password}")
    private String dbPassword;

    // 固定默认参数
    private static final int DEFAULT_PAGE_NUM = 1;
    private static final int DEFAULT_PAGE_SIZE = 1000; // 加大默认分页大小，减少调用次数
    private static final boolean DEFAULT_AUTO_SAVE = true;
    private static final boolean DEFAULT_CLEAN_ZJB_FIRST = true;

    // HTTPS证书处理（测试环境跳过验证）
    static {
        try {
            TrustManager[] trustAllCerts = new TrustManager[]{
                    new X509TrustManager() {
                        @Override
                        public X509Certificate[] getAcceptedIssuers() {
                            return null;
                        }

                        @Override
                        public void checkClientTrusted(X509Certificate[] certs, String authType) throws CertificateException {
                        }

                        @Override
                        public void checkServerTrusted(X509Certificate[] certs, String authType) throws CertificateException {
                        }
                    }
            };

            SSLContext sslContext = SSLContext.getInstance("SSL");
            sslContext.init(null, trustAllCerts, new java.security.SecureRandom());
            HttpsURLConnection.setDefaultSSLSocketFactory(sslContext.getSocketFactory());
            HttpsURLConnection.setDefaultHostnameVerifier((hostname, session) -> true);

            logger.info("HTTPS证书验证已跳过（测试环境配置）");
        } catch (Exception e) {
            logger.error("HTTPS初始化失败", e);
            throw new RuntimeException("HTTPS初始化异常，无法发起请求", e);
        }
    }

    /**
     * 简化版接口：只需要四个核心参数
     *
     * @param request 包含四个必要参数的请求对象
     * @return 包含所有结果的响应
     */
    @PostMapping("/AccessControlSystem_ZS")
    public ApiResponse simplifiedProcess(@RequestBody SimplifiedProcessRequest request) {
        Map<String, Object> result = new HashMap<>();
        List<Map<String, Object>> channelResults = new ArrayList<>();

        try {
            // 步骤1：参数校验
            if (request.getUsername() == null || request.getUsername().trim().isEmpty()) {
                return ApiResponse.error("用户名为空");
            }
            if (request.getPassword() == null || request.getPassword().trim().isEmpty()) {
                return ApiResponse.error("密码为空");
            }
            if (request.getStartSwingTime() == null || request.getStartSwingTime().trim().isEmpty()) {
                return ApiResponse.error("开始时间为空");
            }
            if (request.getEndSwingTime() == null || request.getEndSwingTime().trim().isEmpty()) {
                return ApiResponse.error("结束时间为空");
            }

            logger.info("开始处理 - 用户: {}, 时间范围: {} 至 {}",
                    request.getUsername(), request.getStartSwingTime(), request.getEndSwingTime());

            // 步骤2：获取Token
            OAuthTokenData tokenData = getOAuthAccessToken(request.getUsername(), request.getPassword());
            result.put("token", tokenData.getAccess_token());
            result.put("tokenExpiresIn", tokenData.getExpires_in());

            // 步骤3：查询通道列表（使用默认分页参数）
            ChannelQueryRequest channelReq = new ChannelQueryRequest();
            channelReq.setPageNum(DEFAULT_PAGE_NUM);
            channelReq.setPageSize(DEFAULT_PAGE_SIZE);
            ChannelQueryResponse channelResp = queryDeviceChannel(channelReq, tokenData.getAccess_token());

            if (!channelResp.isSuccess() || !"0".equals(channelResp.getCode())) {
                return ApiResponse.error("通道查询失败: " + channelResp.getErrMsg() + "，错误码: " + channelResp.getCode());
            }

            ChannelQueryData channelData = channelResp.getData();
            if (channelData == null) {
                return ApiResponse.error("未获取到通道数据");
            }

            ChannelItem[] channels = channelData.getPageData();
            if (channels == null || channels.length == 0) {
                return ApiResponse.error("未查询到任何通道数据");
            }

            result.put("totalChannels", channels.length);
            result.put("totalPages", channelData.getTotalPage());

            // 步骤4：清空中间表（使用默认配置）
            if (DEFAULT_CLEAN_ZJB_FIRST) {
                boolean cleanResult = deleteZJB();
                result.put("cleanZjbResult", cleanResult ? "成功" : "失败");
            }

            // 步骤5：逐通道统计并保存（使用默认配置）
            for (ChannelItem channel : channels) {
                Map<String, Object> channelResult = new HashMap<>();
                channelResult.put("channelCode", channel.getChannelCode());
                channelResult.put("channelName", channel.getChannelName());
                channelResult.put("isOnline", channel.getIsOnline() == 1 ? "在线" : "离线");

                if (channel.getChannelCode() == null || channel.getChannelCode().trim().isEmpty()) {
                    channelResult.put("status", "跳过（通道编号为空）");
                    channelResults.add(channelResult);
                    continue;
                }

                // 统计刷卡次数
                int count = countSingleChannelAccess(
                        channel.getChannelCode(),
                        request.getStartSwingTime(),
                        request.getEndSwingTime(),
                        tokenData.getAccess_token()
                );

                channelResult.put("swipeCount", count);

                // 保存到数据库（使用默认配置）
                if (count != -1 && DEFAULT_AUTO_SAVE) {
                    boolean saveResult = batchInsertDepartments(
                            channel.getChannelCode(),
                            channel.getChannelName() != null ? channel.getChannelName() : "未知通道",
                            request.getStartSwingTime(),
                            request.getEndSwingTime(),
                            count
                    );
                    channelResult.put("saveStatus", saveResult ? "成功" : "失败");
                } else {
                    channelResult.put("saveStatus", count == -1 ? "未保存（统计失败）" : "未保存（自动保存未开启）");
                }

                channelResults.add(channelResult);
            }

            result.put("channels", channelResults);
            return ApiResponse.success(result);

        } catch (Exception e) {
            logger.error("处理失败", e);
            return ApiResponse.error("处理失败: " + e.getMessage());
        }
    }

    // ===================== 核心业务方法 =====================
    private OAuthTokenData getOAuthAccessToken(String username, String plainPassword) {
        try {
            logger.info("获取RSA公钥...");
            String publicKey = getRsaPublicKey();

            logger.info("加密用户密码...");
            String encryptedPwd = rsaEncrypt(plainPassword, publicKey);

            logger.info("请求OAuth Token...");
            OAuthTokenRequest tokenRequest = new OAuthTokenRequest(
                    username, encryptedPwd, publicKey, clientId, clientSecret, grantType);
            String responseJson = sendHttpsPostWithAuth(oauthTokenApi, tokenRequest, "TEMP_NO_AUTH");

            OAuthTokenResponse tokenResponse = GSON.fromJson(responseJson, OAuthTokenResponse.class);
            if (tokenResponse == null || !tokenResponse.isSuccess() || tokenResponse.getData() == null) {
                String errMsg = tokenResponse != null ? tokenResponse.getErrMsg() : "未知错误";
                String errCode = tokenResponse != null ? tokenResponse.getCode() : "未知";
                throw new RuntimeException("Token获取失败: " + errMsg + "，错误码: " + errCode);
            }

            logger.info("Token获取成功，有效期: {}秒", tokenResponse.getData().getExpires_in());
            return tokenResponse.getData();
        } catch (Exception e) {
            throw new RuntimeException("获取Token异常: " + e.getMessage(), e);
        }
    }

    private ChannelQueryResponse queryDeviceChannel(ChannelQueryRequest queryRequest, String accessToken) {
        try {
            logger.info("查询通道列表 - 页码: {}, 每页条数: {}", queryRequest.getPageNum(), queryRequest.getPageSize());
            String responseJson = sendHttpsPostWithAuth(channelQueryApi, queryRequest, accessToken);
            ChannelQueryResponse queryResponse = GSON.fromJson(responseJson, ChannelQueryResponse.class);

            if (queryResponse == null) {
                throw new RuntimeException("通道响应为空");
            }

            return queryResponse;
        } catch (Exception e) {
            throw new RuntimeException("查询通道异常: " + e.getMessage(), e);
        }
    }

    private int countSingleChannelAccess(String channelCode, String start, String end, String accessToken) {
        try {
            logger.info("统计通道刷卡次数 - 通道: {}, 时间范围: {} 至 {}", channelCode, start, end);
            AccessControlCountRequest countRequest = new AccessControlCountRequest(start, end, channelCode);
            String responseJson = sendHttpsPostWithAuth(accessCountApi, countRequest, accessToken);
            AccessControlCountResponse countResponse = GSON.fromJson(responseJson, AccessControlCountResponse.class);

            if (countResponse == null) {
                logger.error("统计响应为空 - 通道: {}", channelCode);
                return -1;
            }

            if (!countResponse.isSuccess()) {
                logger.error("统计失败 - 通道: {}, 错误: {}", channelCode, countResponse.getErrMsg());
                return -1;
            }

            return countResponse.getData();
        } catch (Exception e) {
            logger.error("统计异常 - 通道: {}", channelCode, e);
            return -1;
        }
    }

    public boolean batchInsertDepartments(String channelCode, String channelName,
                                          String startTime, String endTime, Number count) {
        try {
            DriverManagerDataSource dataSource = new DriverManagerDataSource();
            dataSource.setDriverClassName("oracle.jdbc.OracleDriver");
            dataSource.setUrl(dbUrl);
            dataSource.setUsername(dbUsername);
            dataSource.setPassword(dbPassword);

            JdbcTemplate jdbcTemplate = new JdbcTemplate(dataSource);

            // 插入中间表
            String sql = "INSERT INTO MJXTJK_ZS_ZJB (" +
                    "CHANNEL_CODE, CHANNEL_NAME, SWING_START_TIME, " +
                    "SWING_END_TIME, SWIPE_COUNT, UPDATE_TIME" +
                    ") VALUES (?, ?, TO_DATE(?, 'yyyy-mm-dd hh24:mi:ss'), " +
                    "TO_DATE(?, 'yyyy-mm-dd hh24:mi:ss'), ?, SYSDATE)";

            // 插入正式表
            String sql3 = "INSERT INTO MJXTJK_ZS (" +
                    "CHANNEL_CODE, CHANNEL_NAME, SWING_START_TIME, " +
                    "SWING_END_TIME, SWIPE_COUNT, UPDATE_TIME" +
                    ") VALUES (?, ?, TO_DATE(?, 'yyyy-mm-dd hh24:mi:ss'), " +
                    "TO_DATE(?, 'yyyy-mm-dd hh24:mi:ss'), ?, SYSDATE)";

            jdbcTemplate.update(sql, channelCode, channelName, startTime, endTime, count);
            jdbcTemplate.update(sql3, channelCode, channelName, startTime, endTime, count);

            logger.info("数据保存成功 - 通道: {}", channelCode);
            return true;
        } catch (Exception e) {
            logger.error("插入数据失败 - 通道: {}", channelCode, e);
            return false;
        }
    }

    public boolean deleteZJB() {
        try {
            DriverManagerDataSource dataSource = new DriverManagerDataSource();
            dataSource.setDriverClassName("oracle.jdbc.OracleDriver");
            dataSource.setUrl(dbUrl);
            dataSource.setUsername(dbUsername);
            dataSource.setPassword(dbPassword);

            JdbcTemplate jdbcTemplate = new JdbcTemplate(dataSource);
            int rowsAffected = jdbcTemplate.update("DELETE FROM MJXTJK_ZS_ZJB");
            logger.info("中间表清空完成，删除行数: {}", rowsAffected);
            return true;
        } catch (Exception e) {
            logger.error("清空中间表失败", e);
            return false;
        }
    }

    // ===================== 工具方法 =====================
    private String getRsaPublicKey() {
        try {
            logger.info("调用公钥接口: {}", publicKeyApi);
            String responseJson = sendHttpsGet(publicKeyApi);
            PublicKeyResponse response = GSON.fromJson(responseJson, PublicKeyResponse.class);

            if (response == null || !response.isSuccess() || response.getData() == null) {
                throw new RuntimeException("公钥获取失败，响应: " + responseJson);
            }

            return response.getData().getPublicKey();
        } catch (Exception e) {
            throw new RuntimeException("公钥处理失败: " + e.getMessage(), e);
        }
    }

    private String rsaEncrypt(String plainText, String publicKey) {
        try {
            byte[] publicKeyBytes = java.util.Base64.getDecoder().decode(publicKey);
            RSAPublicKey rsaPublicKey = (RSAPublicKey) KeyFactory.getInstance(RSA_ALGORITHM)
                    .generatePublic(new X509EncodedKeySpec(publicKeyBytes));

            Cipher cipher = Cipher.getInstance(RSA_ALGORITHM);
            cipher.init(Cipher.ENCRYPT_MODE, rsaPublicKey);

            byte[] plainBytes = plainText.getBytes(StandardCharsets.UTF_8);
            ByteArrayOutputStream out = new ByteArrayOutputStream();
            int offset = 0;
            int inputLen = plainBytes.length;

            // 分段加密
            while (inputLen - offset > 0) {
                int encryptLen = Math.min(inputLen - offset, RSA_MAX_ENCRYPT_BLOCK);
                out.write(cipher.doFinal(plainBytes, offset, encryptLen));
                offset += encryptLen;
            }

            byte[] encryptedBytes = out.toByteArray();
            out.close();

            return java.util.Base64.getEncoder().encodeToString(encryptedBytes);
        } catch (Exception e) {
            throw new RuntimeException("RSA加密失败: " + e.getMessage(), e);
        }
    }

    private String sendHttpsGet(String url) {
        StringBuilder response = new StringBuilder();
        HttpsURLConnection conn = null;
        InputStream in = null;
        BufferedReader reader = null;

        try {
            URL requestUrl = new URL(url);
            conn = (HttpsURLConnection) requestUrl.openConnection();
            conn.setRequestMethod("GET");
            conn.setConnectTimeout(5000);
            conn.setReadTimeout(5000);
            conn.setRequestProperty("Accept", "application/json");
            conn.setRequestProperty("User-Agent", "Mozilla/5.0");

            int responseCode = conn.getResponseCode();
            in = (responseCode == 200) ? conn.getInputStream() : conn.getErrorStream();
            reader = new BufferedReader(new InputStreamReader(in, StandardCharsets.UTF_8));

            String line;
            while ((line = reader.readLine()) != null) {
                response.append(line);
            }

            logger.info("GET请求完成 - 地址: {}, 响应码: {}", url, responseCode);
            return response.toString();
        } catch (Exception e) {
            throw new RuntimeException("GET请求失败 (" + url + "): " + e.getMessage(), e);
        } finally {
            closeResources(reader, in, conn);
        }
    }

    private String sendHttpsPostWithAuth(String url, Object requestBody, String accessToken) {
        if (accessToken == null || accessToken.trim().isEmpty()) {
            throw new IllegalArgumentException("OAuth Token不能为空");
        }

        StringBuilder response = new StringBuilder();
        HttpsURLConnection conn = null;
        OutputStream out = null;
        InputStream in = null;
        BufferedReader reader = null;

        try {
            URL requestUrl = new URL(url);
            conn = (HttpsURLConnection) requestUrl.openConnection();
            conn.setRequestMethod("POST");
            conn.setConnectTimeout(5000);
            conn.setReadTimeout(8000);
            conn.setDoOutput(true);
            conn.setRequestProperty("Content-Type", "application/json;charset=UTF-8");
            conn.setRequestProperty("Accept", "application/json");
            conn.setRequestProperty("Authorization", BEARER_PREFIX + accessToken);

            String requestJson = GSON.toJson(requestBody);
            logger.debug("POST请求体 - 地址: {}, 内容: {}", url, requestJson);

            out = conn.getOutputStream();
            out.write(requestJson.getBytes(StandardCharsets.UTF_8));
            out.flush();

            int responseCode = conn.getResponseCode();
            in = (responseCode >= 200 && responseCode < 300) ? conn.getInputStream() : conn.getErrorStream();
            reader = new BufferedReader(new InputStreamReader(in, StandardCharsets.UTF_8));

            String line;
            while ((line = reader.readLine()) != null) {
                response.append(line);
            }

            if (responseCode >= 300) {
                logger.error("POST请求失败 - 地址: {}, 响应码: {}, 响应: {}", url, responseCode, response);
            } else {
                logger.info("POST请求完成 - 地址: {}, 响应码: {}", url, responseCode);
            }

            return response.toString();
        } catch (Exception e) {
            throw new RuntimeException("POST请求失败 (" + url + "): " + e.getMessage(), e);
        } finally {
            closeResources(reader, in, conn);
            try {
                if (out != null) out.close();
            } catch (IOException e) {
                logger.error("输出流关闭异常", e);
            }
        }
    }

    private void closeResources(Object... resources) {
        if (resources == null || resources.length == 0) return;

        for (Object resource : resources) {
            try {
                if (resource instanceof Closeable) {
                    ((Closeable) resource).close();
                } else if (resource instanceof HttpsURLConnection) {
                    ((HttpsURLConnection) resource).disconnect();
                } else if (resource != null) {
                    logger.warn("不支持关闭的资源类型: {}", resource.getClass().getSimpleName());
                }
            } catch (IOException e) {
                logger.error("资源关闭异常", e);
            }
        }
    }

    // ===================== 实体类 =====================

    /**
     * API响应统一格式
     */
    public static class ApiResponse {
        private int code;
        private String message;
        private Object data;

        private ApiResponse(int code, String message, Object data) {
            this.code = code;
            this.message = message;
            this.data = data;
        }

        public static ApiResponse success(Object data) {
            return new ApiResponse(200, "success", data);
        }

        public static ApiResponse error(String message) {
            return new ApiResponse(500, message, null);
        }

        // getter
        public int getCode() {
            return code;
        }

        public String getMessage() {
            return message;
        }

        public Object getData() {
            return data;
        }
    }

    /**
     * 简化版请求参数（只保留四个必要字段）
     */
    public static class SimplifiedProcessRequest {
        private String username;
        private String password;
        private String startSwingTime;
        private String endSwingTime;

        // getter and setter
        public String getUsername() {
            return username;
        }

        public void setUsername(String username) {
            this.username = username;
        }

        public String getPassword() {
            return password;
        }

        public void setPassword(String password) {
            this.password = password;
        }

        public String getStartSwingTime() {
            return startSwingTime;
        }

        public void setStartSwingTime(String startSwingTime) {
            this.startSwingTime = startSwingTime;
        }

        public String getEndSwingTime() {
            return endSwingTime;
        }

        public void setEndSwingTime(String endSwingTime) {
            this.endSwingTime = endSwingTime;
        }
    }

    // ---------------------- 设备通道接口实体（新增参数） ----------------------
    // 修改通道查询请求实体类，增加unitTypeList参数
    public static class ChannelQueryRequest {
        private int pageNum = 1;
        // 1. 将pageSize初始值从10改为1000
        private int pageSize = 1000;
        // 新增参数：includeSubOwnerCodeFlag，默认值为true
        private boolean includeSubOwnerCodeFlag = true;
        // 2. 新增参数unitTypeList，默认值为包含7的列表
        private List<Integer> unitTypeList = Arrays.asList(7);

        public void setPageNum(int pageNum) {
            this.pageNum = pageNum;
        }

        public void setPageSize(int pageSize) {
            this.pageSize = pageSize;
        }

        public int getPageNum() {
            return pageNum;
        }

        public int getPageSize() {
            return pageSize;
        }

        // 新增参数includeSubOwnerCodeFlag的getter和setter
        public boolean isIncludeSubOwnerCodeFlag() {
            return includeSubOwnerCodeFlag;
        }

        public void setIncludeSubOwnerCodeFlag(boolean includeSubOwnerCodeFlag) {
            this.includeSubOwnerCodeFlag = includeSubOwnerCodeFlag;
        }

        // 3. 新增unitTypeList的getter和setter
        public List<Integer> getUnitTypeList() {
            return unitTypeList;
        }

        public void setUnitTypeList(List<Integer> unitTypeList) {
            this.unitTypeList = unitTypeList;
        }
    }

    private static class ChannelQueryResponse {
        private boolean success;
        private ChannelQueryData data;
        private String code;
        private String errMsg;

        // getter
        public boolean isSuccess() {
            return success;
        }

        public ChannelQueryData getData() {
            return data;
        }

        public String getCode() {
            return code;
        }

        public String getErrMsg() {
            return errMsg;
        }
    }

    private static class ChannelQueryData {
        private ChannelItem[] pageData;
        private int currentPage;
        private int totalPage;
        private int pageSize;
        private int totalRows;

        // getter
        public ChannelItem[] getPageData() {
            return pageData;
        }

        public int getCurrentPage() {
            return currentPage;
        }

        public int getTotalPage() {
            return totalPage;
        }

        public int getPageSize() {
            return pageSize;
        }

        public int getTotalRows() {
            return totalRows;
        }
    }

    private static class ChannelItem {
        private String channelCode;
        private String channelName;
        private Integer id;
        private String deviceCode;
        private Integer isOnline;
        private String channelType;
        private Integer isVirtual;
        private String memo;

        // getter
        public String getChannelCode() {
            return channelCode;
        }

        public String getChannelName() {
            return channelName;
        }

        public Integer getId() {
            return id;
        }

        public String getDeviceCode() {
            return deviceCode;
        }

        public Integer getIsOnline() {
            return isOnline;
        }

        public String getChannelType() {
            return channelType;
        }

        public Integer getIsVirtual() {
            return isVirtual;
        }

        public String getMemo() {
            return memo;
        }
    }

    private static class AccessControlCountRequest {
        private String startSwingTime;
        private String endSwingTime;
        private List<String> channelCodes;

        public AccessControlCountRequest(String startSwingTime, String endSwingTime, String singleChannelCode) {
            this.startSwingTime = startSwingTime;
            this.endSwingTime = endSwingTime;
            this.channelCodes = new ArrayList<>();
            this.channelCodes.add(singleChannelCode);
        }

        // getter
        public String getStartSwingTime() {
            return startSwingTime;
        }

        public String getEndSwingTime() {
            return endSwingTime;
        }

        public List<String> getChannelCodes() {
            return channelCodes;
        }
    }

    private static class AccessControlCountResponse {
        private boolean success;
        private int data;
        private String errMsg;

        // getter
        public boolean isSuccess() {
            return success;
        }

        public int getData() {
            return data;
        }

        public String getErrMsg() {
            return errMsg;
        }
    }

    private static class OAuthTokenRequest {
        private final String grant_type;
        private final String username;
        private final String password;
        private final String client_id;
        private final String client_secret;
        private final String public_key;

        public OAuthTokenRequest(String username, String encryptedPwd, String publicKey,
                                 String clientId, String clientSecret, String grantType) {
            this.username = username;
            this.password = encryptedPwd;
            this.public_key = publicKey;
            this.client_id = clientId;
            this.client_secret = clientSecret;
            this.grant_type = grantType;
        }

        // getter
        public String getGrant_type() {
            return grant_type;
        }

        public String getUsername() {
            return username;
        }

        public String getPassword() {
            return password;
        }

        public String getClient_id() {
            return client_id;
        }

        public String getClient_secret() {
            return client_secret;
        }

        public String getPublic_key() {
            return public_key;
        }
    }

    public static class OAuthTokenData {
        private String access_token;
        private String refresh_token;
        private long expires_in;
        private String token_type;

        // getter
        public String getAccess_token() {
            return access_token;
        }

        public String getRefresh_token() {
            return refresh_token;
        }

        public long getExpires_in() {
            return expires_in;
        }

        public String getToken_type() {
            return token_type;
        }
    }

    private static class OAuthTokenResponse {
        private boolean success;
        private OAuthTokenData data;
        private String code;
        private String errMsg;

        // getter
        public boolean isSuccess() {
            return success;
        }

        public OAuthTokenData getData() {
            return data;
        }

        public String getCode() {
            return code;
        }

        public String getErrMsg() {
            return errMsg;
        }
    }

    private static class PublicKeyResponse {
        private boolean success;
        private PublicKeyData data;
        private String code;
        private String errMsg;

        // getter
        public boolean isSuccess() {
            return success;
        }

        public PublicKeyData getData() {
            return data;
        }

        public String getCode() {
            return code;
        }

        public String getErrMsg() {
            return errMsg;
        }
    }

    private static class PublicKeyData {
        private String publicKey;

        // getter
        public String getPublicKey() {
            return publicKey;
        }
    }
}
